<?php

/**
 * This file is part of the Framework03 PHP framework.
 * @author Walter Tamboer
 * @package Framework03
 */

class FW_Controller_Front
{
    /**
     * @var FW_Controller_Dispatcher_Abstract The instance of the dispatcher that's used in this class.
     */
    private $dispatcher = null;
    
    /**
     * @var FW_Controller_Request_Abstract The request of this controller.
     */
    private $request = null;

    /**
     * @var FW_Controller_Response_Abstract The response of this controller.
     */
    private $response = null;

    /**
     * The plugin manager that belongs to this controller.
     */
    private $pluginManager;

    /**
     * @var string The name of the dispatcher class to use.
     */
    private $dispatcherClass = 'FW_Controller_Dispatcher_Standard';

    /**
     * @var boolean Whether or not to throw exceptions when one did occur or to show the default error controller.
     */
    private $shouldThrowExceptions = false;
    
    /**
     * We need a private constructor because this is a singleton class.
     */
    private function __construct()
    {
    }
    
    /**
     * Gets the instance of this class.
     * @reutrn FW_Controller_Front
     */
    public static function singleton()
    {
        static $instance = null;

        if ($instance == null)
            $instance = new FW_Controller_Front();

        return $instance;
    }
    
    /**
     * Gets the base url of this request.
     * @return string
     */
    public function getBaseUrl()
    {
        if ($this->request != null)
            return $this->request->getBaseUrl();

        return '';
    }

    /**
     * Sets the name of the default controller.
     * @param string $name The name of the default controller.
     * @return FW_Controller_Front
     */
    public function setDefaultController($name)
    {
        $this->getDispatcher()->setDefaultController($name);
        return $this;
    }

    /**
     * Gets the dispatcher used to dispatch requests on this controller.
     * @return FW_Controller_Dispatcher_Abstract
     */
    public function getDispatcher()
    {
        if ($this->dispatcher == null)
            $this->dispatcher = new $this->dispatcherClass();

        return $this->dispatcher;
    }

    /**
     * Gets the plugin manager used by this front controller.
     * @return FW_Controller_PluginManager
     */
    public function getPluginManager()
    {
        if ($this->pluginManager == null)
            $this->pluginManager = new FW_Controller_Plugin_Manager();

        return $this->pluginManager;
    }

    /**
     * Register the given plugin.
     * @param FW_Controller_Plugin_Abstract $plugin
     * @return FW_Controller_Front
     */
    public function registerPlugin(FW_Controller_Plugin_Abstract $plugin)
    {
        $this->getPluginManager()->registerPlugin($plugin);
        return $this;
    }

    /**
     * Unregister the given plugin.
     * @param FW_Controller_Plugin_Abstract $plugin
     * @return FW_Controller_Front
     */
    public function unregisterPlugin(FW_Controller_Plugin_Abstract $plugin)
    {
        $this->getPluginManager()->unregisterPlugin($plugin);
        return $this;
    }
    
    /**
     * Sets whether or not exceptions should be thrown.
     * @param boolean Whether or not to throw exceptions.
     * @return FW_Controller_Front
     */
    public function setThrowExceptions($value)
    {
        $this->shouldThrowExceptions = $value;
        return $this;
    }

    /**
     * Sets the directory of where the controllers of the application can be found.
     * @param string $path The path to the directory of where controllers can be found.
     * @param string $module The name of the module for the controllers.
     * @return FW_Controller_Front
     */
    public function setControllerDirectory($path, $module = null)
    {
        $this->getDispatcher()->setControllerDirectory($path, $module);
        return $this;
    }

    /**
     * Adds a controller router to this front controller so that requests get routed correctly.
     * @param string $name The name of the router.
     * @param FW_Controller_Router_Abstract $router The router that should be added.
     * @return FW_Controller_Front
     */
    public function addRouter($name, FW_Controller_Router_Abstract $router)
    {
        $this->getDispatcher()->addRouter($name, $router);
        return $this;
    }

    /**
     * Dispatches the current request and makes sure that the response is shown on the screen.
     * @param FW_Controller_Request_Abstract $request
     * @param FW_Controller_Response_Abstract $response
     * @return FW_Controller_Response_Abstract Returns the response object.
     */
    public function dispatch(FW_Controller_Request_Abstract $request = null, FW_Controller_Response_Abstract $response = null)
    {
        if ($request == null)
            $this->request = new FW_Controller_Request_Http();
        else
            $this->request = $request;

        if ($response == null)
            $this->response = new FW_Controller_Response_Http();
        else
            $this->response = $response;

        try
        {
            // Get the dispatcher and set the request and response.
            $dispatcher = $this->getDispatcher();
            $dispatcher->setRequest($this->request);
            $dispatcher->setResponse($this->response);

            $pluginManager = $this->getPluginManager();

            if (!$pluginManager->isRegistered('FW_Controller_Plugin_ErrorHandler'))
                $pluginManager->registerPlugin(new FW_Controller_Plugin_ErrorHandler());

            do
            {
                try
                {
                    $this->request->setDispatched(true);

                    $pluginManager->preDispatch($this->request, $this->response);

                    if ($this->request->isDispatched())
                        $dispatcher->dispatch($this->request, $this->response);
                }
                catch (FW_Exception $e)
                {
                    if ($this->shouldThrowExceptions)
                        throw $e;

                    $this->response->setException($e);
                }

                $pluginManager->postDispatch($this->request, $this->response);
            }
            while (!$this->request->isDispatched());
        }
        catch (FW_Exception $e)
        {
            if ($this->shouldThrowExceptions)
                throw $e;

            $this->response->setException($e);
        }
    }
}
